﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Duellum.Core;
using JpLabs.Geometry;
using Duellum.Map;

//TODO: review: extending a namespace in different assemblies?!
namespace Duellum.Core.DuelCoreTypes
{
	public static class Player
	{
		public const string Base = "Player.Base";
	}

	public static class Power
	{
		public const string Base	= "Power.Base";

		public const string Random	= "Power.Random";
		public const string Debug	= "Power.Debug";
		
		public const string Grunt		= "Power.Grunt";
		public const string Demolidor	= "Power.Demolidor";
		public const string Tarrasque	= "Power.Tarrasque";
		
		static public IEnumerable<string> Enumerate() { return DuelCoreTypesExt.EnumerateType(typeof(Power)); }
	}
}

namespace Duellum.Game
{
	public class DuelPlayerType : DuelType
	{
		static public readonly AugProperty PowerProp =
			AugProperty.Create(
				"Power", typeof(DuelTypeId), typeof(DuelPlayerType), (DuelTypeId)Duellum.Core.DuelCoreTypes.Power.Random
			);

		//static public readonly DuelTypeId DefaultBaseTypeId = Duellum.Core.DuelCoreTypes.Player.Base;
		
		public DuelPlayerType(DuelTypeId id) : base(id)
		{
			IsAbstract = true;
		}

		//protected override DuelTypeId GetDefaultBaseType()
		//{
		//    if (Id != DefaultBaseTypeId) return DefaultBaseTypeId;
		//    return base.GetDefaultBaseType();
		//}

		public override BaseDuelObj CreateObj()
		{
			//return new DuelPlayer(new DuelPlayerType(this.GetValue<DuelTypeId>(PowerProp)));
			return new DuelPlayer(this);
		}
	}

	public abstract class DuelPhysicalObj : BaseDuelObj
	{
		static public readonly AugProperty MapProp =
			AugProperty.Create(
				"Map", typeof(DuelMap), typeof(DuelPhysicalObj), null
			);
		static public readonly AugProperty PositionProp =
			AugProperty.Create(
				"Position", typeof(PointInt?), typeof(DuelPhysicalObj), null
				//TODO: Validation: (value.Dimensions == 3)
				//TODO: attach to ValueChanged event
			);
		
		static DuelPhysicalObj()
		{
			PositionProp.GetDefaultBehavior().ValueChanged += PositionProp_ValueChanged;
		}

		static private void PositionProp_ValueChanged(AugObject sender, AugValueChangedEventArgs evArgs)
		{
			var obj = sender as DuelPhysicalObj;
			if (obj == null) return;
			
			var map = obj.Map;
			if (map == null) return;
			
			//Remove old entry from map
			var oldPosition = evArgs.OldValue as PointInt?;
			if (oldPosition.HasValue) {
				var oldEntry = map.Query(oldPosition.Value).Where(e => e.Object == obj).SingleOrDefault();
				map.RemoveEntry(oldEntry);
			}
			
			//Insert object back in map
			var newPosition = evArgs.NewValue as PointInt?;
			if (newPosition.HasValue) {
				map.AddEntry(obj, newPosition.Value);
			}
		}

		public DuelPhysicalObj(DuelType type) : base(type)
		{}

		public DuelMap Map
		{
			get { return (DuelMap)GetValue(MapProp); }
			set { SetValue(MapProp, value); }
		}

		public PointInt? Position
		{
			get { return (PointInt?)GetValue(PositionProp); }
			set { SetValue(PositionProp, value); }
		}
	}

	public class DuelPlayer : DuelPhysicalObj
	{
		static public readonly AugProperty NameProp =
			AugProperty.Create(
				"Name", typeof(string), typeof(DuelPlayer), null
			);
		static public readonly AugProperty FaceDirectionProp =
			AugProperty.Create(
				"FaceDirection", typeof(int), typeof(DuelPlayer), new RandomValueFactory( rnd => rnd.Next(0, 11) )
				//TODO: add coerce function
			);
		//static public readonly AugProperty FaceAngleProp =
		//    AugProperty.Create(
		//        "FaceAngle", typeof(double), typeof(DuelPlayer)
		//        //TODO: add coerce function
		//    );
		static public readonly AugProperty MaxTimeUnitsProp =
			AugProperty.Create(
				"MaxTimeUnits", typeof(int), typeof(DuelPlayer), 100
			);
		static public readonly AugProperty TimeUnitsIncPerTurnProp =
			AugProperty.Create(
				"TimeUnitsIncPerTurn", typeof(int), typeof(DuelPlayer),
				AugValueExt.Create(
					(Func<AugObject,object>)(
						obj => obj.GetValue<int>(MaxTimeUnitsProp) / 2
					),
					null //can't reference to TimeUnitsIncPerTurnProp now, but it doesn`t need to
				)
			);
		
		/// Basic Time costs:
		/// Turn:	1
		/// Move:	15

		public new DuelPlayerType Type { get { return (DuelPlayerType)base.Type; } }
		
		public DuelPlayer(DuelPlayerType playerType) : base(playerType)
		{
		}

		public string Name
		{
			get { return (string)GetValue(NameProp); }
			set { SetValue(NameProp, value); }
		}

		//public double FaceAngle
		//{
		//    get { return (double)GetValue(FaceAngleProp); }
		//    set { SetValue(FaceAngleProp, value); }
		//}
		//public int FaceAngleInt
		//{
		//    get { return (int)Math.Round(this.FaceAngle / (Math.PI / 6)); }
		//    set {
		//        int coerced = value % 12;
		//        if (coerced < 0) coerced += 12;
				
		//        this.FaceAngle = coerced * (Math.PI / 6);
		//    }
		//}

		public double FaceAngle
		{
			get { return this.FaceDirection * (Math.PI / 6); }
			set {
				this.FaceDirection = AngleToDirection(value);
			}
		}
		public int FaceDirection
		{
			get { return (int)GetValue(FaceDirectionProp); }
			set { SetValue(FaceDirectionProp, value); }
		}
		
		static public int AngleToDirection(double angle)
		{
			int direction = (int)Math.Round(angle / (Math.PI / 6));
			if (direction < 0) direction += 12;
			return direction;
		}
		
		public DuelMap PerceivedMap { get; private set; }
		
		public void PerceiveMap(DuelMap gameMap)
		{
			//TODO: code this properly
			this.PerceivedMap = gameMap;
		}
	}
}
